Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defer types like keyof (T & {}) #49696

Merged
merged 7 commits into from
Jul 7, 2022
Merged

Defer types like keyof (T & {}) #49696

merged 7 commits into from
Jul 7, 2022

Conversation

ahejlsberg
Copy link
Member

@ahejlsberg ahejlsberg commented Jun 27, 2022

Ordinarily, types of the form keyof (A & B) are normalized to keyof A | keyof B. However, for types like keyof (T & {}), this becomes keyof T | keyof {}, which becomes just keyof T, losing the fact that undefined and null have been removed from T. With this PR we defer the normalization for intersection types that include {}.

Fixes #49681.

@typescript-bot typescript-bot added Author: Team For Milestone Bug PRs that fix a bug with a specific milestone labels Jun 27, 2022
@ahejlsberg
Copy link
Member Author

@typescript-bot test this
@typescript-bot user test this inline
@typescript-bot run dt
@typescript-bot perf test faster

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jun 27, 2022

Heya @ahejlsberg, I've started to run the diff-based user code test suite on this PR at ab04c57. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jun 27, 2022

Heya @ahejlsberg, I've started to run the extended test suite on this PR at ab04c57. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jun 27, 2022

Heya @ahejlsberg, I've started to run the parallelized Definitely Typed test suite on this PR at ab04c57. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jun 27, 2022

Heya @ahejlsberg, I've started to run the abridged perf test suite on this PR at ab04c57. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

@ahejlsberg
The results of the perf run you requested are in!

Here they are:

Comparison Report - main..49696

Metric main 49696 Delta Best Worst
Angular - node (v14.15.1, x64)
Memory used 335,271k (± 0.01%) 335,222k (± 0.01%) -49k (- 0.01%) 335,150k 335,264k
Parse Time 2.06s (± 0.62%) 2.06s (± 0.63%) -0.00s (- 0.19%) 2.04s 2.09s
Bind Time 0.90s (± 0.52%) 0.90s (± 0.52%) -0.00s (- 0.00%) 0.89s 0.91s
Check Time 5.74s (± 0.44%) 5.75s (± 0.53%) +0.01s (+ 0.17%) 5.68s 5.83s
Emit Time 6.38s (± 0.46%) 6.37s (± 0.59%) -0.01s (- 0.19%) 6.28s 6.46s
Total Time 15.08s (± 0.21%) 15.07s (± 0.40%) -0.00s (- 0.02%) 14.94s 15.22s
Compiler-Unions - node (v14.15.1, x64)
Memory used 192,683k (± 0.02%) 192,671k (± 0.02%) -12k (- 0.01%) 192,571k 192,732k
Parse Time 0.85s (± 0.70%) 0.85s (± 0.56%) +0.01s (+ 0.83%) 0.84s 0.86s
Bind Time 0.57s (± 0.78%) 0.57s (± 0.70%) -0.00s (- 0.17%) 0.56s 0.58s
Check Time 7.63s (± 0.65%) 7.63s (± 0.58%) -0.00s (- 0.01%) 7.55s 7.72s
Emit Time 2.50s (± 0.49%) 2.51s (± 0.75%) +0.00s (+ 0.16%) 2.45s 2.54s
Total Time 11.55s (± 0.51%) 11.56s (± 0.50%) +0.01s (+ 0.05%) 11.42s 11.66s
Monaco - node (v14.15.1, x64)
Memory used 325,624k (± 0.00%) 325,621k (± 0.01%) -2k (- 0.00%) 325,578k 325,669k
Parse Time 1.58s (± 0.60%) 1.57s (± 0.49%) -0.01s (- 0.44%) 1.56s 1.59s
Bind Time 0.79s (± 0.59%) 0.79s (± 0.62%) +0.00s (+ 0.51%) 0.78s 0.80s
Check Time 5.67s (± 0.48%) 5.68s (± 0.40%) +0.01s (+ 0.12%) 5.62s 5.72s
Emit Time 3.36s (± 0.84%) 3.36s (± 0.33%) 0.00s ( 0.00%) 3.35s 3.40s
Total Time 11.40s (± 0.36%) 11.40s (± 0.17%) +0.01s (+ 0.05%) 11.37s 11.45s
TFS - node (v14.15.1, x64)
Memory used 288,764k (± 0.01%) 288,757k (± 0.00%) -7k (- 0.00%) 288,737k 288,786k
Parse Time 1.32s (± 1.69%) 1.34s (± 1.30%) +0.02s (+ 1.37%) 1.29s 1.37s
Bind Time 0.78s (± 4.28%) 0.77s (± 3.88%) -0.02s (- 2.30%) 0.74s 0.85s
Check Time 5.31s (± 0.48%) 5.31s (± 0.56%) -0.01s (- 0.13%) 5.27s 5.39s
Emit Time 3.59s (± 2.23%) 3.49s (± 1.53%) -0.10s (- 2.87%) 3.43s 3.69s
Total Time 11.00s (± 0.80%) 10.89s (± 0.68%) -0.11s (- 1.00%) 10.79s 11.08s
material-ui - node (v14.15.1, x64)
Memory used 446,364k (± 0.01%) 446,357k (± 0.00%) -6k (- 0.00%) 446,328k 446,415k
Parse Time 1.86s (± 0.39%) 1.87s (± 0.58%) +0.01s (+ 0.54%) 1.85s 1.90s
Bind Time 0.73s (± 0.00%) 0.73s (± 1.11%) -0.00s (- 0.00%) 0.71s 0.75s
Check Time 13.09s (± 0.78%) 13.05s (± 0.64%) -0.04s (- 0.32%) 12.89s 13.31s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 15.68s (± 0.66%) 15.65s (± 0.61%) -0.03s (- 0.21%) 15.48s 15.95s
xstate - node (v14.15.1, x64)
Memory used 541,027k (± 0.01%) 541,220k (± 0.00%) +193k (+ 0.04%) 541,180k 541,275k
Parse Time 2.59s (± 0.23%) 2.60s (± 0.62%) +0.00s (+ 0.15%) 2.58s 2.66s
Bind Time 1.15s (± 1.42%) 1.16s (± 1.00%) +0.00s (+ 0.17%) 1.13s 1.18s
Check Time 1.53s (± 0.49%) 1.54s (± 0.73%) +0.01s (+ 0.46%) 1.52s 1.57s
Emit Time 0.07s (± 4.66%) 0.07s (± 3.14%) -0.00s (- 2.74%) 0.07s 0.08s
Total Time 5.35s (± 0.28%) 5.37s (± 0.55%) +0.02s (+ 0.43%) 5.33s 5.47s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-210-generic
Architecturex64
Available Memory16 GB
Available Memory15 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v14.15.1, x64)
Scenarios
  • Angular - node (v14.15.1, x64)
  • Compiler-Unions - node (v14.15.1, x64)
  • Monaco - node (v14.15.1, x64)
  • TFS - node (v14.15.1, x64)
  • material-ui - node (v14.15.1, x64)
  • xstate - node (v14.15.1, x64)
Benchmark Name Iterations
Current 49696 10
Baseline main 10

Developer Information:

Download Benchmark

@typescript-bot
Copy link
Collaborator

Heya @ahejlsberg, I've run the RWC suite on this PR - assuming you're on the TS core team, you can view the resulting diff here.

@typescript-bot
Copy link
Collaborator

@ahejlsberg
Great news! no new errors were found between main..refs/pull/49696/merge

Comment on lines 33 to 35
type K04 = keyof void; // never
type K05 = keyof undefined; // never
type K06 = keyof null; // never
type K05 = keyof undefined; // string | number | symbol
type K06 = keyof null; // string | number | symbol
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, as a user - this doesn't quite make any sense. It's super hard for me to reason about what kind of runtime behavior this is supposed to represent. I've also checked it with this PR and this creates an unsoundness and might lead to runtime errors like here:

function test<T, K extends keyof T>(t: T, k: K) {
    return t[k]; // oops, runtime error if we allow the call below
}

test(null, 'foo') // OK with this patch, error with 4.7

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the new 4.8 behavior, test itself should have an error in its implementation due to not having a { } constraint.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First experiment wasn't optimal, now deferring types like keyof (T & {}) such that effects of removing undefined and null are properly reflected.

@ahejlsberg ahejlsberg changed the title keyof undefined and keyof null same as keyof never Defer types like keyof (T & {}) Jun 28, 2022
@ahejlsberg
Copy link
Member Author

@typescript-bot test this
@typescript-bot user test this inline
@typescript-bot run dt
@typescript-bot perf test faster

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jun 28, 2022

Heya @ahejlsberg, I've started to run the parallelized Definitely Typed test suite on this PR at b457868. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jun 28, 2022

Heya @ahejlsberg, I've started to run the diff-based user code test suite on this PR at b457868. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jun 28, 2022

Heya @ahejlsberg, I've started to run the abridged perf test suite on this PR at b457868. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jun 28, 2022

Heya @ahejlsberg, I've started to run the extended test suite on this PR at b457868. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

@ahejlsberg
Great news! no new errors were found between main..refs/pull/49696/merge

@typescript-bot
Copy link
Collaborator

Heya @ahejlsberg, I've run the RWC suite on this PR - assuming you're on the TS core team, you can view the resulting diff here.

@typescript-bot
Copy link
Collaborator

@ahejlsberg
The results of the perf run you requested are in!

Here they are:

Comparison Report - main..49696

Metric main 49696 Delta Best Worst
Angular - node (v14.15.1, x64)
Memory used 335,277k (± 0.01%) 335,256k (± 0.01%) -21k (- 0.01%) 335,136k 335,293k
Parse Time 2.07s (± 0.49%) 2.06s (± 0.59%) -0.00s (- 0.19%) 2.04s 2.10s
Bind Time 0.90s (± 0.84%) 0.91s (± 0.54%) +0.00s (+ 0.22%) 0.90s 0.92s
Check Time 5.75s (± 0.61%) 5.76s (± 0.31%) +0.01s (+ 0.19%) 5.73s 5.81s
Emit Time 6.41s (± 0.86%) 6.40s (± 0.58%) -0.01s (- 0.11%) 6.32s 6.50s
Total Time 15.13s (± 0.54%) 15.13s (± 0.27%) -0.00s (- 0.01%) 15.04s 15.23s
Compiler-Unions - node (v14.15.1, x64)
Memory used 192,558k (± 0.13%) 192,681k (± 0.02%) +123k (+ 0.06%) 192,622k 192,777k
Parse Time 0.85s (± 1.23%) 0.85s (± 0.85%) -0.00s (- 0.12%) 0.84s 0.87s
Bind Time 0.58s (± 1.21%) 0.57s (± 0.97%) -0.01s (- 1.21%) 0.56s 0.59s
Check Time 7.68s (± 0.63%) 7.69s (± 0.74%) +0.01s (+ 0.16%) 7.53s 7.79s
Emit Time 2.51s (± 0.94%) 2.50s (± 0.69%) -0.01s (- 0.40%) 2.46s 2.55s
Total Time 11.63s (± 0.58%) 11.62s (± 0.61%) -0.01s (- 0.08%) 11.40s 11.73s
Monaco - node (v14.15.1, x64)
Memory used 325,618k (± 0.01%) 325,612k (± 0.00%) -5k (- 0.00%) 325,576k 325,636k
Parse Time 1.59s (± 1.10%) 1.59s (± 0.90%) 0.00s ( 0.00%) 1.57s 1.63s
Bind Time 0.80s (± 0.81%) 0.80s (± 0.81%) -0.00s (- 0.00%) 0.79s 0.82s
Check Time 5.69s (± 0.72%) 5.68s (± 0.53%) -0.01s (- 0.26%) 5.59s 5.73s
Emit Time 3.38s (± 0.75%) 3.36s (± 0.37%) -0.02s (- 0.59%) 3.33s 3.39s
Total Time 11.46s (± 0.56%) 11.43s (± 0.32%) -0.03s (- 0.30%) 11.34s 11.49s
TFS - node (v14.15.1, x64)
Memory used 288,784k (± 0.01%) 288,792k (± 0.01%) +9k (+ 0.00%) 288,752k 288,836k
Parse Time 1.33s (± 1.71%) 1.33s (± 2.12%) -0.00s (- 0.15%) 1.29s 1.40s
Bind Time 0.75s (± 2.25%) 0.78s (± 4.70%) +0.03s (+ 3.98%) 0.74s 0.86s
Check Time 5.32s (± 0.46%) 5.33s (± 0.46%) +0.01s (+ 0.23%) 5.26s 5.38s
Emit Time 3.53s (± 2.45%) 3.61s (± 1.83%) +0.07s (+ 2.09%) 3.47s 3.71s
Total Time 10.94s (± 0.94%) 11.05s (± 0.48%) +0.11s (+ 1.00%) 10.91s 11.14s
material-ui - node (v14.15.1, x64)
Memory used 446,260k (± 0.04%) 446,377k (± 0.00%) +118k (+ 0.03%) 446,340k 446,443k
Parse Time 1.88s (± 0.46%) 1.88s (± 0.66%) -0.00s (- 0.21%) 1.85s 1.90s
Bind Time 0.73s (± 0.96%) 0.73s (± 1.46%) -0.01s (- 0.68%) 0.71s 0.76s
Check Time 13.09s (± 0.52%) 13.15s (± 0.73%) +0.06s (+ 0.45%) 12.93s 13.33s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 15.70s (± 0.43%) 15.75s (± 0.65%) +0.05s (+ 0.32%) 15.50s 15.96s
xstate - node (v14.15.1, x64)
Memory used 541,030k (± 0.00%) 541,039k (± 0.00%) +9k (+ 0.00%) 540,994k 541,077k
Parse Time 2.60s (± 0.51%) 2.60s (± 0.51%) -0.00s (- 0.08%) 2.58s 2.63s
Bind Time 1.17s (± 1.30%) 1.15s (± 1.62%) -0.02s (- 2.13%) 1.12s 1.21s
Check Time 1.54s (± 0.63%) 1.54s (± 0.58%) +0.00s (+ 0.32%) 1.52s 1.56s
Emit Time 0.07s (± 4.92%) 0.07s (± 4.66%) -0.00s (- 1.35%) 0.07s 0.08s
Total Time 5.39s (± 0.62%) 5.37s (± 0.41%) -0.02s (- 0.35%) 5.33s 5.44s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-210-generic
Architecturex64
Available Memory16 GB
Available Memory15 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v14.15.1, x64)
Scenarios
  • Angular - node (v14.15.1, x64)
  • Compiler-Unions - node (v14.15.1, x64)
  • Monaco - node (v14.15.1, x64)
  • TFS - node (v14.15.1, x64)
  • material-ui - node (v14.15.1, x64)
  • xstate - node (v14.15.1, x64)
Benchmark Name Iterations
Current 49696 10
Baseline main 10

Developer Information:

Download Benchmark

@Andarist
Copy link
Contributor

I've noticed that this PR didn't introduce the reported issue from #49681 as a test case. Is that alright? Or perhaps just an accidental omission?

Comment on lines +273 to +275
// We allow an unconstrained object of a generic type `T` to be indexed by a key of type `keyof T`
// without a check that the object is non-undefined and non-null. This is safe because `keyof T`
// is `never` (meaning no possible keys) for any `T` that includes `undefined` or `null`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this sort of an unusual thing to "defer" the error reporting to the call site? I'm not totally sure what I think about this yet but my first thought was that it's a special case in the algorithm that might be hard to explain to people.

Copy link
Contributor

@Andarist Andarist Jun 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this build on top of the fact that T[never] is always OK and never is always assignable to everything so perhaps this fits the existing rules just OK. I'll take consolation in the fact @RyanCavanaugh's first instinct was that those should raise errors too 😅

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the test and the comment to capture the behavior for future reference. It's nothing new actually. We have the same behavior in 4.7 with the old NonNullable<T> based on a conditional type. This PR ensures we carry it forward in 4.8 with NonNullable<T> based on T & {}.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure - but I'm not thinking here from the PoV of NonNullable at all. My confusion/concern was solely about unconstrained generics and your changes from #49119, I've assumed that given those changes this indexed access would error nowadays. Since the base constraint of unconstrained T is unknown it feels off that we can use indexed access on it without refining the type somehow first

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may seem surprising at first, but it is actually quite meaningful because we can permit t[k] without requiring local proof that t is non-undefined and non-null. Such proof would add unnecessary overhead for instantiations where the type argument for T is already known not to be undefined or null.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Milestone Bug PRs that fix a bug with a specific milestone
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

"cannot be used to index type" error when indexing NonNullable of a generic type in typescript 4.8
4 participants